package com.ayf.payment.game.api.utils;

import com.swwx.charm.commons.lang.utils.LogPortal;
import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.zip.*;

/**
 * @Description: zip工具类
 * @Author: zh-liang
 * @Date : 2020-03-30 11:36
 */
public class ZipUtils {
    //文件打包下载
    public static void downLoadFiles(String path, String fileName, HttpServletResponse response) throws Exception {
        String filePath=path + "/" + fileName + ".zip";
        try {
            File[] files = new File(path).listFiles();
            //创建一个临时压缩文件，我们会把文件流全部注入到这个文件中 这里的文件你可以自定义是.rar还是.zip*/
            File file = new File( filePath);
            if (!file.exists()) {
                file.createNewFile();
            }
            response.reset();
            response.getWriter();
            //创建文件输出流
            FileOutputStream fos = new FileOutputStream(file);
            //打包的方法我们会用到ZipOutputStream这样一个输出流,所以这里我们把输出流转换一下
            ZipOutputStream zipOut = new ZipOutputStream(fos);
            //这个方法接受的就是一个所要打包文件的集合， 还有一个ZipOutputStream
            zipFile(files, zipOut);
            zipOut.close();
            fos.close();
            downloadZip(file, response);
        } catch (Exception e) {
            LogPortal.error("文件打包下载发送异常，文件名称:{}",e,filePath);
        }finally {
            deleteDir(path);
        }
    }

    /**
     * 把接受的全部文件打成压缩包
     */
    public static void zipFile(File[] files, ZipOutputStream outputStream) {
        for (File file : files) {
            zipFile(file, outputStream);
        }
    }

    public static void downloadZip(File file, HttpServletResponse response) {
        try {
            // 以流的形式下载文件。
            InputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 清空response
            response.reset();
            response.setContentType("application/octet-stream");
            //如果输出的是中文名的文件，在此处就要用URLEncoder.encode方法进行处理
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            OutputStream toClient = response.getOutputStream();
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 根据输入的文件与输出流对文件进行打包
     *
     * @param inputFile
     * @param outputStream
     */
    public static void zipFile(File inputFile, ZipOutputStream outputStream) {
        try {
            if (inputFile.exists()) {
                //如果是目录的话这里是不采取操作的， 至于目录的打包正在研究中
                if (inputFile.isFile()) {
                    FileInputStream IN = new FileInputStream(inputFile);
                    BufferedInputStream bins = new BufferedInputStream(IN, 512);
                    //org.apache.tools.zip.ZipEntry
                    ZipEntry entry = new ZipEntry(inputFile.getName());
                    outputStream.putNextEntry(entry);
                    // 向压缩文件中输出数据
                    int nNumber;
                    byte[] buffer = new byte[512];
                    while ((nNumber = bins.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, nNumber);
                    }
                    // 关闭创建的流对象
                    bins.close();
                    IN.close();
                } else {
                    try {
                        File[] files = inputFile.listFiles();
                        assert files != null;
                        for (File file : files) {
                            zipFile(file, outputStream);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void deleteDir(String dirPath) {
        File file = new File(dirPath);
        if (!file.isFile()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File value : files) {
                    deleteDir(value.getAbsolutePath());
                }
            }
        }
        file.delete();
    }

    /**
     * 解压缩zip包
     *
     * @param zipFilePath        zip文件的全路径
     * @param unzipFilePath      解压后的文件保存的路径
     * @param includeZipFileName 解压后的文件保存的路径是否包含压缩文件的文件名。true-包含；false-不包含
     */
    @SuppressWarnings("unchecked")
    public static void unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName, Charset charset) throws IOException {
        if (StringUtils.isEmpty(zipFilePath) || StringUtils.isEmpty(unzipFilePath)) {
            //            throw new ParameterException(ICommonResultCode.PARAMETER_IS_NULL);
        }
        File zipFile=new File(zipFilePath);
        //如果解压后的文件保存路径包含压缩文件的文件名，则追加该文件名到解压路径
        if (includeZipFileName) {
            String fileName=zipFile.getName();
            if (StringUtils.isNotEmpty(fileName)) {
                fileName=fileName.substring(0, fileName.lastIndexOf("."));
            }
            unzipFilePath=unzipFilePath + File.separator + fileName;
        }
        //创建解压缩文件保存的路径
        File unzipFileDir=new File(unzipFilePath);
        if (!unzipFileDir.exists() || !unzipFileDir.isDirectory()) {
            unzipFileDir.mkdirs();
        }

        //开始解压
        ZipEntry entry=null;
        String entryFilePath=null, entryDirPath=null;
        File entryFile=null, entryDir=null;
        int index=0, count=0, bufferSize=1024;
        byte[] buffer=new byte[bufferSize];
        BufferedInputStream bis=null;
        BufferedOutputStream bos=null;
        ZipFile zip=new ZipFile(zipFile,charset);
        Enumeration<ZipEntry> entries=(Enumeration<ZipEntry>)zip.entries();
        //循环对压缩包里的每一个文件进行解压
        while (entries.hasMoreElements()) {
            entry=entries.nextElement();
            //构建压缩包中一个文件解压后保存的文件全路径
            entryFilePath=unzipFilePath + File.separator + entry.getName();
            //构建解压后保存的文件夹路径
            index=entryFilePath.lastIndexOf(File.separator);
            if (index != -1) {
                entryDirPath=entryFilePath.substring(0, index);
            } else {
                entryDirPath="";
            }
            entryDir=new File(entryDirPath);
            //如果文件夹路径不存在，则创建文件夹
            if (!entryDir.exists() || !entryDir.isDirectory()) {
                entryDir.mkdirs();
            }

            //创建解压文件
            entryFile=new File(entryFilePath);
            if (entryFile.exists()) {
                //检测文件是否允许删除，如果不允许删除，将会抛出SecurityException
                SecurityManager securityManager=new SecurityManager();
                securityManager.checkDelete(entryFilePath);
                //删除已存在的目标文件
                entryFile.delete();
            }

            //写入文件
            bos=new BufferedOutputStream(new FileOutputStream(entryFile));
            bis=new BufferedInputStream(zip.getInputStream(entry));
            while ((count=bis.read(buffer, 0, bufferSize)) != -1) {
                bos.write(buffer, 0, count);
            }
            bos.flush();
            bos.close();
        }
    }

    /**
     * 对文件或文件目录进行压缩
     *
     * @param srcPath     要压缩的源文件路径。如果压缩一个文件，则为该文件的全路径；如果压缩一个目录，则为该目录的顶层目录路径
     * @param zipPath     压缩文件保存的路径。注意：zipPath不能是srcPath路径下的子文件夹
     * @param zipFileName 压缩文件名
     * @throws Exception
     */
    public static void zip(String srcPath, String zipPath, String zipFileName,boolean isClearSourceFile) throws IOException {
        if (StringUtils.isEmpty(srcPath) || StringUtils.isEmpty(zipPath) || StringUtils.isEmpty(zipFileName)) {
            throw new NullPointerException(
                    "参数为空！srcPath:[" + srcPath + "], zipPath:[" + zipPath + "], zipFileName:[" + zipFileName + "]");
        }
        CheckedOutputStream cos=null;
        ZipOutputStream zos=null;
        try {
            File srcFile=new File(srcPath);
            //判断压缩文件保存的路径是否为源文件路径的子文件夹，如果是，则抛出异常（防止无限递归压缩的发生）
            if (srcFile.isDirectory() && zipPath.indexOf(srcPath) != -1) {
                throw new RuntimeException("压缩文件保存的路径是源文件路径的子文件夹，会导致无限递归压缩");
            }
            //判断压缩文件保存的路径是否存在，如果不存在，则创建目录
            File zipDir=new File(zipPath);
            if (!zipDir.exists() || !zipDir.isDirectory()) {
                zipDir.mkdirs();
            }
            //创建压缩文件保存的文件对象
            String zipFilePath=zipPath + File.separator + zipFileName;
            File zipFile=new File(zipFilePath);
            if (zipFile.exists()) {
                //检测文件是否允许删除，如果不允许删除，将会抛出SecurityException
                //                SecurityManager securityManager = new SecurityManager();
                //                securityManager.checkDelete(zipFilePath);
                //删除已存在的目标文件
                zipFile.delete();
            }
            cos=new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
            zos=new ZipOutputStream(cos);
            //如果只是压缩一个文件，则需要截取该文件的父目录
            String srcRootDir=srcPath;
            if (srcFile.isFile()) {
                int index=srcPath.lastIndexOf(File.separator);
                if (index != -1) {
                    srcRootDir=srcPath.substring(0, index);
                }
            }
            //调用递归压缩方法进行目录或文件压缩
            //增加一个跟压缩包名字一样的子目录
            String[] split = zipFileName.split("\\.");
            zip(srcRootDir, srcFile, zos ,split[0]);
            //清空源目录文件
            if (isClearSourceFile){
                if (srcFile.isDirectory() && srcFile.list().length > 0) {
                    for (File tmp : srcFile.listFiles()) {
                        tmp.delete();
                    }
                }
            }
            zos.flush();
        } catch (IOException e) {
            throw e;
        } finally {
            try {
                if (zos != null) {
                    zos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 递归压缩文件夹
     *
     * @param srcRootDir 压缩文件夹根目录的子路径
     * @param file       当前递归压缩的文件或目录对象
     * @param zos        压缩文件存储对象
     * @throws Exception
     */
    private static void zip(String srcRootDir, File file, ZipOutputStream zos,String subSrc) throws IOException {
        if (file == null) {
            return;
        }
        //如果是文件，则直接压缩该文件
        if (file.isFile()) {
            //获取文件相对于压缩文件夹根目录的子路径
            String subPath=file.getAbsolutePath();
            int index=subPath.indexOf(srcRootDir);
            if (index != -1) {
                subPath=subPath.substring(srcRootDir.length() + File.separator.length());
            }
            //压缩到子目录
            subPath = "/"+ subSrc +"/" + subPath;
            zip(subPath, new FileInputStream(file), zos);
        }
        //如果是目录，则压缩整个目录
        else {
            //压缩目录中的文件或子目录
            File[] childFileList=file.listFiles();
            for (int n=0; n < childFileList.length; n++) {
                childFileList[n].getAbsolutePath().indexOf(file.getAbsolutePath());
                zip(srcRootDir, childFileList[n], zos,subSrc);
            }
        }
    }

    /**
     * 压缩指定InputStream
     *
     * @param entryName
     * @param inputStream
     * @param zos
     * @throws Exception
     */
    private static void zip(String entryName, InputStream inputStream, ZipOutputStream zos) throws IOException {
        {
            int count, bufferLen=1024;
            byte data[]=new byte[bufferLen];
            ZipEntry entry=new ZipEntry(entryName);
            zos.putNextEntry(entry);
            BufferedInputStream bis=new BufferedInputStream(inputStream);
            while ((count=bis.read(data, 0, bufferLen)) != -1) {
                zos.write(data, 0, count);
            }
            bis.close();
            zos.closeEntry();
        }
    }
}
